/*
 * RC4, Adler32, Base64 and UTF-8 implementations. Simple and strong ;-)
 */
//RC4 Cipher algorithm
function RC4(_key) {
	// Swap function
	this.swapS = function(_i, _j) {
		temp = this.s[_i];
		this.s[_i] = this.s[_j];
		this.s[_j] = temp;
	}
	// Encrypting/decrypting
	this.proceed = function(_data) {
		var result = new Array();
		var i = 0;
		var j = 0;
		for ( var d = 0; d < _data.length; d++) {
			i = (i + 1) % 256;
			j = (j + this.s[i]) % 256;
			this.swapS(i, j);
			result.push(this.s[(this.s[i] + this.s[j]) % 256] ^ _data[d]);
		}
		return result;
	}
	this.reset=function(){
		this.s=this.initialS.slice();
	}
	// Constructor - key-scheduling
	this.s = new Array();
	this.initialS;
	var utf8=new Utf8();
	this.key=utf8.encode(_key);
	for ( var i = 0; i < 256; i++) {
		this.s.push(i);
	}
	var j = 0;
	for ( var i = 0; i < 256; i++) {
		j = (j + this.s[i] + this.key[i % this.key.length]) % 256;
		this.swapS(i, j);
	}
	this.initialS=this.s.slice();
}
// Adler32 checkusm algorithm
function Adler32() {
	this.MOD_ADLER = 65521;
	this.a = 1;
	this.b = 0;
	this.update = function(_data) {
		for ( var i = 0; i < _data.length; i++) {
			this.a = (this.a + _data[i]) % this.MOD_ADLER;
			this.b = (this.b + this.a) % this.MOD_ADLER;
		}
	}
	this.getValueA = function() {
		return this.a;
	}
	this.getValueB = function() {
		return this.b;
	}
	this.reset = function() {
		this.a = 1;
		this.b = 0;
	}
}
// Base64
function Base64() {
	this.codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	this.decode = function(_base64) {
		var i = 0;
		var binary = new Array();
		if (_base64) {
			while (i < _base64.length) {
				var value = 0;
				var shift = 18;
				var number=0;
				while (shift >= 0 && i < _base64.length) {
					var code = this.codes.indexOf(_base64[i]);
					i++;
					if(code>=0){
						if(code==64){
							break;
						}
						value |= code << shift;
						shift -= 6;
						number++;
					}
				}
				shift=16;
				for(var j=0;j<(number-1);j++){
					binary.push((value >> shift) & 0xFF);
					shift-=8;
				}
			}
		}
		return binary;
	}

	this.encode = function(_binary) {
		var base64 = "";
		if (_binary) {
			var i = 0;
			while (i < _binary.length) {
				var shift = 16;
				var triplet = 0;
				var readNumber = 0;
				while (i < _binary.length && shift >= 0) {
					triplet |= _binary[i] << shift;
					i++;
					readNumber++;
					shift -= 8;
				}
				shift = 18;
				for ( var j = 0; j <= readNumber; j++) {
					var index = (triplet >> shift) & 0x3F;
					base64 += this.codes[index];
					shift -= 6;
				}
				for ( var j = 3; j > readNumber; j--) {
					base64 += "=";
				}
			}
			return base64;
		}
	}
}

// Utf8 encoder/decoder
function Utf8() {
	this.firstMasks = [ 0, 0, 0x1F, 0xF, 0x7 ];
	this.decode = function(_bytes) {
		var utf8 = "";
		var i = 0;
		while (i < _bytes.length) {
			if ((_bytes[i] & 0x80) < 1) {
				utf8 += String.fromCharCode(_bytes[i]);
			} else {
				var numOfBytes = 2;
				if (_bytes[i] >= 0xF0) {
					numOfBytes = 4;
				} else if (_bytes[i] >= 0xE0) {
					numOfBytes = 3;
				}
				var shift = (numOfBytes - 1) * 6;
				var value = (_bytes[i] & this.firstMasks[numOfBytes]) << shift;
				while (shift > 0) {
					i++;
					shift -= 6;
					value |= (_bytes[i] & 0x3F) << shift;
				}
				utf8 += String.fromCharCode(value);
			}
			i++;
		}
		return utf8;
	}

	this.encode = function(_utf8) {
		var bytes = new Array();
		for ( var i = 0; i < _utf8.length; i++) {
			code = _utf8.charCodeAt(i);
			if (code <= 0x7F) {
				bytes.push(code);
			} else {
				var byte = 0xC0;
				var numOfBytes = 2;
				if (code > 0xFFFF) {
					numOfBytes = 4;
					byte = 0xF0;
				} else if (code > 0x7FF) {
					numOfBytes = 3;
					byte = 0xE0;
				}
				shift = (6 * (numOfBytes - 1));
				byte |= (code >> shift) & this.firstMasks[numOfBytes];
				bytes.push(byte);
				while (shift > 0) {
					shift -= 6;
					bytes.push(0x80 | (code >> shift) & 0x3F);
				}
			}
		}
		return bytes;
	}
}
